#include <stdio.h>
#include <stdlib.h>
#include <string>

#include "hidclass.hpp"

#define JOIN( X, Y ) JOIN2(X,Y)
#define JOIN2( X, Y ) X##Y

namespace static_assert
{
    template <bool> struct STATIC_ASSERT_FAILURE;
    template <> struct STATIC_ASSERT_FAILURE<true> { enum { value = 1 }; };

    template<int x> struct static_assert_test{};
}

#define COMPILE_ASSERT(x) \
    typedef ::static_assert::static_assert_test<\
        sizeof(::static_assert::STATIC_ASSERT_FAILURE< (bool)( x ) >)>\
            JOIN(_static_assert_typedef, __LINE__)

template<uint32_t clk, uint32_t baudRate, uint32_t div,
    bool error_stop = !(div < 16)>
class BaudRateT2
{
private:
    enum
    {
        condition    = (((clk / baudRate) % (div + 1)) < 3)
    };
    typedef BaudRateT2<clk, baudRate, div + 1> next_type;
public:
    enum
    {
        error       = error_stop,               
        DIV_X_EN    = 1,
        DIV_X_ONE   = 0,
        DIVIDER_X   = (condition ? div
                    : next_type::DIVIDER_X),
        BRD         = (condition? (clk / baudRate / (div + 1) - 2)
                    : next_type::BRD)
    };
};

template<uint32_t clk, uint32_t baudRate, uint32_t div>
class BaudRateT2<clk, baudRate, div, true>
{
public:
    enum
    {
        error       = true,
        DIV_X_EN    = 0,
        DIV_X_ONE   = 0,
        DIVIDER_X   = 0,
        BRD         = 0
    };
};



template<uint32_t clk, uint32_t baudRate>
class BaudRateT1
{
private:
    enum
    {
        condition   = !(clk / baudRate - 2 > 0xFFFF)
    };
    typedef BaudRateT2<clk, baudRate, 8> next_type;
public:
    enum
    {
        error       = (condition ? 0
                    : next_type::error),
        DIV_X_EN    = (condition ? 1
                    : next_type::DIV_X_EN),
        DIV_X_ONE   = (condition ? 1
                    : next_type::DIV_X_ONE),
        DIVIDER_X   = (condition ? 0
                    : next_type::DIVIDER_X),
        BRD         = (condition ? (clk / baudRate - 2)
                    : next_type::BRD)
    };
};

template<uint32_t clk, uint32_t baudRate>
class BaudRateT0
{
private:
    enum
    {
        condition   = ((clk / baudRate) % 16 < 3)
    };
    typedef BaudRateT1<clk, baudRate> next_type;
public:
    enum
    {
        error       = (condition ? ( (clk / baudRate/16 - 2) > 0xffff)
                    : next_type::error),
        DIV_X_EN    = (condition ? 0
                    : next_type::DIV_X_EN),
        DIV_X_ONE   = (condition ? 0
                    : next_type::DIV_X_ONE),
        DIVIDER_X   = (condition ? 0
                    : next_type::DIVIDER_X),
        BRD         = (condition ? (clk / baudRate/16 - 2)
                    : next_type::BRD)
    };
};


template<uint32_t clk, uint32_t baudRate>
class BaudRateT
{
private:
    typedef BaudRateT0<clk, baudRate> next_type;
public:
    enum
    {
        error       = next_type::error,
        DIV_X_EN    = next_type::DIV_X_EN,
        DIV_X_ONE   = next_type::DIV_X_ONE,
        DIVIDER_X   = next_type::DIVIDER_X,
        BRD         = next_type::BRD,
        REG_VALUE   = ((uint32_t)BRD)
                    + ((uint32_t)DIVIDER_X << 24)
                    + ((uint32_t)DIV_X_ONE << 28)
                    + ((uint32_t)DIV_X_EN << 29)
    };
};


template<class T>
void ShowBytes(T& v, const char* desc)
{
    printf("%s\n",desc);
    uint8_t *p = (uint8_t *)&v;
    for(int i=0;i<sizeof(T);i++){
        printf("%02x ",p[i]);
        if((i &0xf) == 0xf){
            printf("\n");
        }
    }
    printf("\n");
}

using namespace USBTemp;

typedef Interface<
        0, 
        HidDescriptor<
            HidSubClassBoot,
            HidProtocolMouse,
            AppendEndPoint< EndPointDesc< EpIntIn<1> >,
            AppendEndPoint< EndPointDesc< EpIntOut<1> >
                > >
        >
    >
Interface0;

typedef Interface<
        1, 
        HidDescriptor<
            HidSubClassBoot,
            HidProtocolKeyboard,
            AppendEndPoint< EndPointDesc< EpIntIn<2> >,
            AppendEndPoint< EndPointDesc< EpIntOut<2> >
                > >
        >
    >
Interface1;



typedef UsbDevice<
    DeviceDesc< 
        UsbVer<0x200>,
        DevClass<0,0,0>,
        PacketSize<64>,
        DevID<0x1985,0x1017>,
        DevVer<0x101>,
        DevSering<1,2,3>,
        ConfigCount<1>
        >,
    ConfigDesc<
        AppendInterface< Interface0 ,
        AppendInterface< Interface1
            > >
        >
>MyDev;


const uint8_t HidReportDescriptor[] =
{
    0x05, 0x8c, // USAGE_PAGE (Generic Desktop)
    0x09, 0x01, // USAGE (0)
    0xa1, 0x01, // COLLECTION (Application)
	0x09, 0x01, //     USAGE(01)
    0x15, 0x00, //     LOGICAL_MINIMUM (0)
    0x25, 0xff, //     LOGICAL_MAXIMUM (255)
    0x95, 0x40, //     REPORT_COUNT (8)
    0x75, 0x08, //     REPORT_SIZE (8)
    0x81, 0x02, //     INPUT (Data,Var,Abs)
	0x09, 0x02, //     USAGE(02)
    0x91, 0x02, //     OUTPUT (Data,Var,Abs)
    0xc0        // END_COLLECTION
};

template <typename T, int N>
char (*RtlpNumberOf( T (&)[N] ))[N];

#define RTL_NUMBER_OF_V2(A) (sizeof(*RtlpNumberOf(A)))

#define ARRAYSIZE(A)    RTL_NUMBER_OF_V2(A)

template<int T>
class GetArrSize
{
public:
    enum{ v = T};
};

template<class T, int size>
uint8_t ttfun(T (&a)[size])
{
    printf("Descriptor length: %d\n",GetArrSize<sizeof(HidReportDescriptor)>::v);
    ShowBytes(a,"xx:");
    return 0;
}

int main()
{
    const MyDev dev;
    MyDev::Initialize();
    ttfun(HidReportDescriptor);
    ShowBytes(dev.devDesc,"Device descriptor:");
    ShowBytes(dev.configDesc,"Config descriptor:");
   typedef BaudRateT<22118400, 115200> baudrate_type;
   COMPILE_ASSERT(baudrate_type::error==0);
   printf("error = %d\n"
          "Register value of UA_BAUD = 0x%08x\n"
          "DIV_X_N = %d\n"
          "DIV_X_ONE = %d\n"
          "DIVIER_X = %d\n"
          "BRD = %d\n",
           
          baudrate_type::error,      //指示计算是否出错
          baudrate_type::REG_VALUE,  //BAUD寄存器的32位值
  
          baudrate_type::DIV_X_EN,   //BAUD寄存器的DIV_X_EN栏位
          baudrate_type::DIV_X_ONE,  //BAUD寄存器的DIV_X_ONE栏位
          baudrate_type::DIVIDER_X,  //BAUD寄存器的DIVIDER_X栏位
          baudrate_type::BRD         //BAUD寄存器的BRD栏位
          );
}
